iT邦幫忙

2026 iThome 鐵人賽

DAY 13
0

其實因為前面的彩格範例是突然想到的,所以今天才開始弄接下來主題想要做到的內容。今天是畫天空的顏色。

common.hlsl

// 根據 Vertex ID 取得 Fullscreen Triangle 的頂點位置
float2 GetFullscreenTrianglePosition(uint vertexId)
{
    // 定義覆蓋整個畫面的三角形三個頂點
    static const float2 positions[3] =
    {
        float2(-1.0f, -1.0f),
        float2(-1.0f, 3.0f),
        float2(3.0f, -1.0f)
    };
    
    // 回傳目前 Vertex ID 對應的位置
    return positions[vertexId];
}
sky_gradient.hlsl

#include "common.hlsl"

// 定義 Vertex Shader 傳給 Pixel Shader 的資料
struct VSOutput
{
    // 儲存頂點的裁切空間位置
    float4 position : SV_POSITION;

    // 儲存垂直方向的天空漸層值
    float gradient : TEXCOORD0;
};

// 根據 Vertex ID 建立 Fullscreen Triangle 的頂點資料
VSOutput VSMain(uint vertexId : SV_VertexID)
{
    // 取得目前 Vertex ID 對應的 Fullscreen Triangle 位置
    const float2 position = GetFullscreenTrianglePosition(vertexId);

    // 建立 Vertex Shader 輸出資料
    VSOutput output;

    // 將二維位置轉換成裁切空間座標
    output.position = float4(position, 0.0f, 1.0f);

    // 將 Y 座標從 [-1, 1] 映射到 [0, 1] 作為天空漸層值
    output.gradient = position.y * 0.5f + 0.5f;

    // 回傳 Vertex Shader 輸出
    return output;
}

// 根據垂直漸層產生天空顏色
float4 PSMain(VSOutput input) : SV_TARGET
{
    // 定義接近地平線的天空顏色
    const float3 horizonColor = float3(0.65f, 0.85f, 1.0f);

    // 定義接近天頂的天空顏色
    const float3 zenithColor = float3(0.08f, 0.35f, 0.85f);

    // 將插值後的漸層值限制在 [0, 1] 範圍
    const float gradient = saturate(input.gradient);

    // 根據漸層值在地平線與天頂顏色之間進行線性插值
    const float3 color = lerp(horizonColor, zenithColor, gradient);

    // 輸出不透明的天空顏色
    return float4(color, 1.0f);
}

main.cpp

#include <cstdlib>
#include <exception>
#include <stdexcept>
#include <directx/d3dx12_core.h>

#include "graphics_engine.h"
#include "my_engine.h"
#include "skyline_debugger.h"
#include "system.h"

namespace
{
    void initRootSignature(RootSignature& rs)
    {
        rs.init(D3D12_FILTER_MIN_MAG_MIP_LINEAR,
            D3D12_TEXTURE_ADDRESS_MODE_WRAP,
            D3D12_TEXTURE_ADDRESS_MODE_WRAP,
            D3D12_TEXTURE_ADDRESS_MODE_WRAP);
    }

    D3D12_GRAPHICS_PIPELINE_STATE_DESC createSkyPipelineStateDescription(
        ID3D12RootSignature* rootSignature,
        ID3DBlob* vertexShader,
        ID3DBlob* pixelShader)
    {
        if (rootSignature == nullptr)
            throw std::invalid_argument("SkyPipeline: Root signature is required.");
        if (vertexShader == nullptr)
            throw std::invalid_argument("SkyPipeline: Vertex shader is required.");
        if (pixelShader == nullptr)
            throw std::invalid_argument("SkyPipeline: Pixel shader is required.");

        D3D12_GRAPHICS_PIPELINE_STATE_DESC description{};

        description.InputLayout = {nullptr, 0};

        description.pRootSignature = rootSignature;

        // 把 ID3DBlob 裡的 vertex shader 跟 pixel shader 放進 description
        description.VS = CD3DX12_SHADER_BYTECODE(vertexShader);
        description.PS = CD3DX12_SHADER_BYTECODE(pixelShader);

        description.RasterizerState = CD3DX12_RASTERIZER_DESC(D3D12_DEFAULT);
        description.RasterizerState.CullMode = D3D12_CULL_MODE_NONE;
        description.BlendState = CD3DX12_BLEND_DESC(D3D12_DEFAULT);
        description.DepthStencilState = CD3DX12_DEPTH_STENCIL_DESC(D3D12_DEFAULT);
        description.DepthStencilState.DepthEnable = FALSE;
        description.DepthStencilState.DepthWriteMask = D3D12_DEPTH_WRITE_MASK_ZERO;
        description.DepthStencilState.StencilEnable = FALSE;
        description.SampleMask = UINT_MAX;
        description.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE;
        description.NumRenderTargets = 1;
        description.RTVFormats[0] = DXGI_FORMAT_R8G8B8A8_UNORM;
        description.DSVFormat = DXGI_FORMAT_D32_FLOAT;
        description.SampleDesc.Count = 1;
        return description;
    }

    void initPipelineState(PipelineState& pipelineState, RootSignature& rs, Shader& vs, Shader& ps)
    {
        const D3D12_GRAPHICS_PIPELINE_STATE_DESC psoDesc =
            createSkyPipelineStateDescription(rs.get(), vs.getCompiledBlob(), ps.getCompiledBlob());
        pipelineState.init(psoDesc);
    }

}

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow)
{
    try
    {
        SkylineDebugger::Initialize("DayX_Sky");

        // 基礎圖形初始化
        SKYLINE_LOG(INFO) << "[DayX_Sky] Initializing window...";
        initWindow(hInstance, hPrevInstance, lpCmdLine, nCmdShow, TEXT("Game"));
        if (g_hWnd == nullptr)
            throw std::runtime_error("DayX_Sky: Failed to create the application window.");

        SKYLINE_LOG(INFO) << "[DayX_Sky] Initializing graphics engine...";
        GraphicsEngine graphicsEngine;
        graphicsEngine.init(g_hWnd, FRAME_BUFFER_W, FRAME_BUFFER_H);
        SkylineDebugger::ConfigureD3D12(graphicsEngine.getD3DDevice());

        // 1. RootSignature 初始化
        SKYLINE_LOG(INFO) << "[DayX_Sky] Initializing root signature...";
        RootSignature rootSignature;
        initRootSignature(rootSignature);

        SKYLINE_LOG(INFO) << "[DayX_Sky] Compiling shaders...";
        Shader vs, ps;
        vs.loadVS("assets/shaders/sky_gradient.hlsl", "VSMain");
        ps.loadPS("assets/shaders/sky_gradient.hlsl", "PSMain");

        SKYLINE_LOG(INFO) << "[DayX_Sky] Creating pipeline state...";
        PipelineState pipelineState;
        initPipelineState(pipelineState, rootSignature, vs, ps);

        SKYLINE_LOG(INFO) << "[DayX_Sky] Initialization completed.";
        RenderContext& renderContext = graphicsEngine.getRenderContext();

        bool isFirstFrame = true;
        while (dispatchWindowMessage())
        {
            if (isFirstFrame)
                SKYLINE_LOG(INFO) << "[DayX_Sky] Rendering first frame...";

            graphicsEngine.beginRender();

            renderContext.setRootSignature(rootSignature);
            renderContext.setPipelineState(pipelineState);
            renderContext.setPrimitiveTopology(D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
            renderContext.draw(3);

            graphicsEngine.endRender();

            SkylineDebugger::LogD3D12Messages(graphicsEngine.getD3DDevice());
            if (isFirstFrame)
            {
                SKYLINE_LOG(INFO) << "[DayX_Sky] First frame presented.";
                isFirstFrame = false;
            }
        }

        SkylineDebugger::Shutdown();
        return EXIT_SUCCESS;
    }
    catch (const std::exception& exception)
    {
        SkylineDebugger::ShowFatalError("DayX_Sky initialization failed", exception);
        SkylineDebugger::Shutdown();
        return EXIT_FAILURE;
    }
    catch (...)
    {
        SkylineDebugger::ShowFatalError("DayX_Sky initialization failed", "An unknown fatal error occurred.");
        SkylineDebugger::Shutdown();
        return EXIT_FAILURE;
    }

}

結果


上一篇
Day 12:StructuredBuffer
系列文
因為 AI 看不懂老舊程式,只好乖乖從零開始學 DirectX 12 與 HLSL13
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言